package com.dhms.uikit

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.widget.FrameLayout
import android.widget.TextView
import com.dhms.uikit.databinding.LineChartViewBinding
import com.github.mikephil.charting.components.LimitLine
import com.github.mikephil.charting.components.MarkerView
import com.github.mikephil.charting.components.XAxis
import com.github.mikephil.charting.components.YAxis
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import com.github.mikephil.charting.formatter.ValueFormatter
import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.listener.ChartTouchListener
import com.github.mikephil.charting.listener.OnChartGestureListener
import com.github.mikephil.charting.renderer.XAxisRenderer
import com.github.mikephil.charting.utils.MPPointF
import com.github.mikephil.charting.utils.Transformer
import com.github.mikephil.charting.utils.Utils
import com.github.mikephil.charting.utils.ViewPortHandler
import java.util.*

class LineChartView : FrameLayout {

    private lateinit var _binding: LineChartViewBinding
    val binding get() = _binding

    private var lineColor = 0

    private var isDateFormat = false

    private var markerKey: String? = null

    private val chartDataSet: LinkedHashMap<String, LineDataSet> = LinkedHashMap()

    constructor(context: Context) : super(context) {
        init(context)
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        init(context, attrs)
    }

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
        context,
        attrs,
        defStyle
    )

    private fun init(context: Context, attrs: AttributeSet? = null) {
        _binding = LineChartViewBinding.inflate(LayoutInflater.from(context))

        if (attrs != null) {
            val typedArray = context.obtainStyledAttributes(attrs, R.styleable.LineChartView)
            lineColor = typedArray.getColor(
                R.styleable.LineChartView_chart_line_color,
                0
            )
            isDateFormat = typedArray.getBoolean(R.styleable.LineChartView_chart_x_is_date, false)
            markerKey = typedArray.getString(R.styleable.LineChartView_chart_marker)
            typedArray.recycle()
        }

        initChart()

        addView(binding.root)
    }

    private fun initChart() {
        val chartView = binding.chartView

        chartView.xAxis.position = XAxis.XAxisPosition.BOTTOM
        chartView.xAxis.setDrawGridLines(false)
        chartView.legend.isEnabled = false
        chartView.setNoDataText(context.getString(R.string.no_data))
        chartView.description.isEnabled = false
        chartView.axisLeft.setDrawGridLines(false)
        chartView.axisRight.isEnabled = false
        chartView.marker = Maker(context, markerKey, isDateFormat)
        chartView.xAxis.labelCount = 3
        chartView.isScaleYEnabled = false

        if (isDateFormat) {
            chartView.xAxis.valueFormatter = dateFormatter
            chartView.extraBottomOffset = 15f
            chartView.setXAxisRenderer(
                CustomXAxisRenderer(
                    chartView.viewPortHandler,
                    chartView.xAxis,
                    chartView.getTransformer(YAxis.AxisDependency.LEFT)
                )
            )
            chartView.onChartGestureListener = object : OnChartGestureListener {
                override fun onChartGestureStart(
                    me: MotionEvent?,
                    lastPerformedGesture: ChartTouchListener.ChartGesture?
                ) {
                }

                override fun onChartLongPressed(me: MotionEvent?) {}
                override fun onChartDoubleTapped(me: MotionEvent?) {}
                override fun onChartSingleTapped(me: MotionEvent?) {}
                override fun onChartFling(
                    me1: MotionEvent?,
                    me2: MotionEvent?,
                    velocityX: Float,
                    velocityY: Float
                ) {
                }

                override fun onChartScale(me: MotionEvent?, scaleX: Float, scaleY: Float) {}
                override fun onChartTranslate(me: MotionEvent?, dX: Float, dY: Float) {}

                // TODO("Time format.")
                override fun onChartGestureEnd(
                    me: MotionEvent?,
                    lastPerformedGesture: ChartTouchListener.ChartGesture?
                ) {
//                    val xRange = chartView.visibleXRange.toInt()
//                    when {
//                        xRange > 60 -> {}
//                    }
//                    chartView.xAxis.labelCount = 3
                }
            }
        }
    }

    /**
     * @sample
     * val dataObj = arrayListOf(1, 2, 3, 4, 5, 6)
     * val entries = dataObj.map {
     *     Entry(1656585708F + it * 10000, it.toFloat())
     * }
     * val dataSet = LineDataSet(entries, "")
     * setChart(dataSet)
     */
    fun setChart(dataSet: LineDataSet?) {
        if (dataSet == null) {
            this.chartDataSet.remove("running_points")
        } else {
            if (dataSet.entryCount > 1) {
                dataSet.setDrawValues(false)
                dataSet.setDrawCircles(false)
            }
            dataSet.color =
                if (lineColor != 0)
                    lineColor else context.getColorFromAttr(com.google.android.material.R.attr.colorPrimary)
            this.chartDataSet["running_points"] = dataSet
        }

        drawChart()
    }

    /**
     * Sets the points which are drawn on the chart.
     */
    fun setPoints(dataSet: LineDataSet?) {
        if (dataSet == null) {
            this.chartDataSet.remove("fault_points")
        } else {
            this.chartDataSet["fault_points"] = dataSet
        }
        drawChart()
    }

    private fun drawChart() {
        val chartView = binding.chartView
        val data = LineData()
        this.chartDataSet.values.forEach {
            data.addDataSet(it)
        }
        chartView.data = if (data.dataSetCount == 0) null else data
        chartView.requestLayout()
    }

    /**
     * Add limit line to chart.
     */
    fun setLimitLine(lines: List<ChartLimitLine>) {
        Log.d("1158", lines.toString())
        val yAxis = binding.chartView.axisLeft
        yAxis.removeAllLimitLines()
        lines.forEach {
            val limitLine = if (it.label == null) LimitLine(
                it.value,
                it.value.toString()
            ) else LimitLine(it.value, it.label + ": " + it.value)
            limitLine.enableDashedLine(10f, 10f, 10f)
            limitLine.lineColor = it.color
            limitLine.textColor = it.color
            limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP
            yAxis.addLimitLine(limitLine)
        }
    }

    // TODO("Add and highlight a point.")
    fun addPoint(toAddEntry: Entry) {
        // 高亮icon
//        toAddEntry.icon = 高亮icon
//        binding.chartView.data.addDataSet(LineDataSet(listOf(toAddEntry), ""))
    }

    companion object {
        @SuppressLint("ViewConstructor")
        class Maker(
            context: Context,
            private val makerKey: String? = null,
            private val isDateFormat: Boolean
        ) :
            MarkerView(context, R.layout.chart_marker_view) {

            private var mOffset: MPPointF? = null

            private val markerViewTitle = findViewById<TextView>(R.id.chart_marker_title)
            private val markerViewKey = findViewById<TextView>(R.id.chart_marker_key)
            private val markerViewValue = findViewById<TextView>(R.id.chart_marker_value)

            override fun getOffset(): MPPointF {
                if (mOffset == null) {
                    mOffset = MPPointF((-(width / 2)).toFloat(), (-height).toFloat())
                }

                return mOffset!!
            }

            override fun refreshContent(e: Entry?, highlight: Highlight?) {
                if (isDateFormat) {
                    markerViewTitle.text = e?.x?.let { Date((it * 1000).toLong()).toFormatDate() }
                    markerViewTitle.visibility = VISIBLE
                } else {
                    markerViewTitle.text = null
                    markerViewTitle.visibility = GONE
                }
                markerViewKey.text =
                    if (makerKey != null) context.getString(R.string.colon, makerKey) else null
                markerViewValue.text = e?.y.toString()

                super.refreshContent(e, highlight)
            }
        }

        val dateFormatter = object : ValueFormatter() {

            private var dateFormat = "yyyy-MM-dd HH:mm:ss"

            // TODO("Time format.")
            fun setDateFormat(format: String) {
                dateFormat = format
            }

            override fun getFormattedValue(value: Float): String {
                val emissionsMilliSince1970Time = (value * 1000).toLong()

                return Date(emissionsMilliSince1970Time).toFormatDate(dateFormat).split(" ")
                    .joinToString("\n")
            }
        }

        // TODO("The wrapped lines need requesting layout.")
        class CustomXAxisRenderer(
            viewPortHandler: ViewPortHandler,
            xAxis: XAxis,
            trans: Transformer
        ) : XAxisRenderer(viewPortHandler, xAxis, trans) {

            override fun drawLabel(
                c: Canvas,
                formattedLabel: String,
                x: Float,
                y: Float,
                anchor: MPPointF,
                angleDegrees: Float
            ) {
                val lines = formattedLabel.split("\n")
                var i = 0
                lines.forEach { line ->
                    val offset = i++ * mAxisLabelPaint.textSize
                    Utils.drawXAxisValue(
                        c,
                        line,
                        x,
                        y + offset,
                        mAxisLabelPaint,
                        anchor,
                        angleDegrees
                    )
                }
            }
        }
    }

    data class ChartLimitLine(
        val label: String?,
        val value: Float,
        val color: Int,
    )
}